from PyQt5.QtCore import QThread, pyqtSignal
import logging
import os
import struct
import numpy as np
# 新增导入优化的文件处理器
try:
    from optimized_file_processor import OptimizedFileProcessor
    HAS_OPTIMIZED_PROCESSOR = True
except ImportError:
    HAS_OPTIMIZED_PROCESSOR = False
    logging.warning("优化的文件处理器不可用，使用原始方式")
import gc
import psutil


class FileLoaderThread(QThread):
    """文件加载线程，用于在后台加载大文件，避免UI卡顿
    
    新增功能：
    - 内存监控和管理
    - 智能块大小调整
    - 增强的异常处理
    
    Signals:
        progress_signal: 发送进度信息，参数为进度值(0-100)和状态描述
        finished_signal: 加载完成信号，发送读取的帧数据
        error_signal: 错误信号，发送错误信息
    """
    progress_signal = pyqtSignal(int, str, str)  # 进度值, 状态描述, 详细信息
    finished_signal = pyqtSignal(list)  # 读取完成的帧列表
    error_signal = pyqtSignal(str)  # 错误信息
    
    def __init__(self, frame_processor, file_path, use_optimized=True):
        super().__init__()
        self.frame_processor = frame_processor
        self.file_path = file_path
        self.is_cancelled = False
        self.logger = logging.getLogger('frame_analyzer')
        self.use_optimized = use_optimized and HAS_OPTIMIZED_PROCESSOR
        self.optimized_processor = None
        
    def check_memory_usage(self):
        """检查内存使用情况"""
        try:
            memory = psutil.virtual_memory()
            if memory.percent > 85:
                self.logger.warning(f"内存使用率高: {memory.percent:.1f}%")
                gc.collect()
                return False
            return True
        except:
            return True
            
    def run(self):
        """线程执行函数"""
        try:
            self.logger.warning(f"开始在后台线程中加载文件: {self.file_path}")
            
            # 检查文件大小
            file_size = os.path.getsize(self.file_path)
            self.logger.info(f"文件大小: {file_size / (1024**3):.2f} GB")
            
            # 检查内存
            if not self.check_memory_usage():
                self.error_signal.emit("内存不足，请关闭其他程序后重试")
                return
            
            def progress_callback(value, status, detail=None):
                if self.is_cancelled:
                    raise Exception("操作已取消")
                self.progress_signal.emit(value, status, detail or "")
            
            # 如果文件大于500MB且有优化处理器，使用优化方式
            if file_size > 500 * 1024 * 1024 and self.use_optimized:
                self.logger.info("使用优化的文件处理器")
                self._load_with_optimized_processor(progress_callback)
                return
            
            # 否则使用原始方式（但仍进行内存检查）
            self._load_with_original_processor(progress_callback)
            
        except MemoryError as e:
            self.logger.error(f"内存错误: {str(e)}")
            gc.collect()
            self.error_signal.emit("内存不足，文件太大无法加载")
        except Exception as e:
            self.logger.error(f"文件加载线程出错: {str(e)}")
            if str(e) != "操作已取消":
                self.error_signal.emit(str(e))
    
    def _load_with_optimized_processor(self, progress_callback):
        """使用优化处理器加载文件"""
        try:
            self.optimized_processor = OptimizedFileProcessor()
            
            # 检测文件格式
            _, file_ext = os.path.splitext(self.file_path)
            frame_format = None
            
            # 如果是DAT文件，尝试检测格式
            if file_ext.lower() == '.dat':
                frame_format, _ = self.optimized_processor.detect_frame_format(self.file_path)
            
            # 流式读取文件
            all_frames = []
            chunk_count = 0
            
            for frames_chunk in self.optimized_processor.read_file_streaming(
                self.file_path, frame_format, progress_callback
            ):
                if self.is_cancelled:
                    break
                    
                all_frames.extend(frames_chunk)
                chunk_count += 1
                
                # 每处理10个块检查一次内存
                if chunk_count % 10 == 0:
                    if not self.check_memory_usage():
                        self.logger.warning("内存压力大，暂停并清理")
                        gc.collect()
                        
                    # 如果帧数过多，考虑限制
                    if len(all_frames) > 2000000:  # 200万帧
                        self.logger.warning("帧数过多，停止读取")
                        break
            
            if not self.is_cancelled:
                self.logger.info(f"优化加载完成，共读取 {len(all_frames)} 帧")
                self.finished_signal.emit(all_frames)
                
        except Exception as e:
            self.logger.error(f"优化加载失败: {str(e)}")
            raise
        finally:
            if self.optimized_processor:
                self.optimized_processor.safe_cleanup()
    
    def _load_with_original_processor(self, progress_callback):
        """使用原始处理器加载文件（带内存保护）"""
        try:
            # 检查文件扩展名，如果是.dat文件，优先使用FMT1029格式读取
            _, file_ext = os.path.splitext(self.file_path)
            if file_ext.lower() == '.dat':
                try:
                    # 通知进度
                    progress_callback(10, f"使用FMT格式读取DAT文件")
                    
                    # 使用特定格式读取DAT文件
                    frames = self._read_dat_with_fmt1029(progress_callback)
                    
                    if not self.is_cancelled:
                        self.logger.warning(f"DAT文件使用FMT格式加载完成，共读取 {len(frames)} 帧")
                        self.finished_signal.emit(frames)
                        return
                except Exception as dat_error:
                    self.logger.warning(f"使用FMT格式读取DAT文件失败，尝试标准方式读取: {str(dat_error)}")
                    # 如果FMT1029格式读取失败，回退到标准读取方式
            
            # 使用标准方式读取文件
            frames = self.frame_processor.read_frame(self.file_path, progress_callback)
            
            if not self.is_cancelled:
                self.logger.warning(f"文件加载完成，共读取 {len(frames)} 帧")
                self.finished_signal.emit(frames)
        except Exception as e:
            self.logger.error(f"标准加载失败: {str(e)}")
            raise
    
    def _read_dat_with_fmt1029(self, progress_callback):
        """使用FMT1029格式读取DAT文件（增强内存管理）"""
        try:
            # 定义DAT文件格式(1092字节每帧)
            FMT1029 = np.dtype([
                ('sync_word', '>u4'),
                ('length', '>u4'),
                ('zero', '>u4'),
                ('t1', '>u4'),
                ('t2', '>u4'),
                ('frame_count', '>u4'),
                ('帧检验结果', '>u4'),
                ('帧同步状态', '>u4'),
                ('位滑状态', '>u4'),  # 0：无位滑；1：有位滑。
                ('virtual_channel', '>u4'),  # 借用空闲字段 存放 数据虚拟信道标识
                ('frame_len', '>u4'),
                ('同步字长度', '>u4'),
                ('decode_status', '>u4'),  # 译码状态
                ('同步字状态', '>u4'),
                ('f_001', '>u4', 2),
                ('f896', [
                    ('sync', '>u4'),
                    ('h1', '>u2'),
                    ('h2', '>u4'),
                    ('payload', 'S886')
                ]),
                ('f128', 'V128'),
                ('synn_end', '>u4'),
            ])
            
            # 读取数据文件
            file_size = os.path.getsize(self.file_path)
            progress_callback(20, f"读取DAT文件", f"文件大小: {file_size / (1024 * 1024):.2f} MB")
            
            # 如果文件太大，分块读取
            if file_size > 1 * 1024 * 1024 * 1024:  # 1GB
                return self._read_dat_chunked(progress_callback, FMT1029)
            
            # 使用NumPy读取整个文件
            data = np.fromfile(self.file_path, dtype=FMT1029)
            progress_callback(50, f"已读取 {len(data)} 帧", "正在转换数据格式")
            
            # 设置虚拟信道值 - 注意：np.fromfile返回的数组是可写的，不需要copy()
            data['virtual_channel'] = data['f896']['h1'] & 0x3F  # 从f896字段中获取虚拟信道
            
            # 转换为标准的帧列表格式
            frames = []
            for i, frame in enumerate(data):
                # 更新进度
                if i % max(1, len(data) // 20) == 0:
                    progress = int(50 + (i / len(data) * 40))
                    progress_callback(progress, f"正在处理第 {i+1}/{len(data)} 帧", "转换格式")
                    
                    # 如果发现进程被取消，立即退出
                    if self.is_cancelled:
                        raise Exception("操作已取消")

                # 构造完整的VCDU数据区（从第64字节开始的1024字节）
                # 使用struct.pack保持正确的字节序
                vcdu_data = (
                    struct.pack('>I', int(frame['f896']['sync'])) +      # 第64-67字节：帧同步头
                    struct.pack('>H', int(frame['f896']['h1'])) +        # 第68-69字节：版本号+航天器标识  
                    struct.pack('>I', int(frame['f896']['h2'])) +        # 第70-73字节：包含虚拟信道帧计数
                    frame['f896']['payload'].tobytes()                   # 第74-1091字节：数据
                )
                
                # 将NumPy标量转换为Python原生类型
                frames.append({
                    'sync_word': int(frame['sync_word']),
                    'frame_count': int(frame['frame_count']),
                    'virtual_channel': int(frame['virtual_channel']),
                    'decode_status': int(frame['decode_status']),
                    'payload': vcdu_data,  # 使用完整的VCDU数据区
                    'frame_len': int(frame['frame_len']),
                    'frame_sync_status': int(frame['帧同步状态']),
                    'bit_slip_status': int(frame['位滑状态']),
                    '帧检验结果': int(frame['帧检验结果']),
                    '同步字状态': int(frame['同步字状态'])
                })

            # 完成处理
            progress_callback(95, f"格式处理完成", f"共读取 {len(frames)} 帧")
            return frames

        except MemoryError:
            self.logger.error("内存不足，无法加载整个文件")
            raise
        except Exception as e:
            self.logger.error(f"格式读取DAT文件失败: {str(e)}")
            raise
    
    def _read_dat_chunked(self, progress_callback, dtype):
        """分块读取大DAT文件"""
        frames = []
        file_size = os.path.getsize(self.file_path)
        frame_size = dtype.itemsize
        chunk_size = 50 * 1024 * 1024  # 50MB chunks
        chunk_frames = chunk_size // frame_size
        total_frames = file_size // frame_size
        
        with open(self.file_path, 'rb') as f:
            processed = 0
            while processed < total_frames:
                # 读取一个块
                frames_to_read = min(chunk_frames, total_frames - processed)
                chunk_data = f.read(frames_to_read * frame_size)
                
                if not chunk_data:
                    break
                
                # 解析块
                chunk_array = np.frombuffer(chunk_data, dtype=dtype).copy()
                
                # 处理数据
                chunk_array['virtual_channel'] = chunk_array['f896']['h1'] & 0x3F
                
                # 转换格式
                for i, frame in enumerate(chunk_array):
                    if self.is_cancelled:
                        raise Exception("操作已取消")
                        
                                            # 构造完整的VCDU数据区（从第64字节开始的1024字节）
                        # 使用struct.pack保持正确的字节序
                        vcdu_data = (
                            struct.pack('>I', int(frame['f896']['sync'])) +      # 第64-67字节：帧同步头
                            struct.pack('>H', int(frame['f896']['h1'])) +        # 第68-69字节：版本号+航天器标识  
                            struct.pack('>I', int(frame['f896']['h2'])) +        # 第70-73字节：包含虚拟信道帧计数
                            frame['f896']['payload'].tobytes()                   # 第74-1091字节：数据
                        )
                        
                        frames.append({
                            'sync_word': int(frame['sync_word']),
                            'frame_count': int(frame['frame_count']),
                            'virtual_channel': int(frame['virtual_channel']),
                            'decode_status': int(frame['decode_status']),
                            'payload': vcdu_data,  # 使用完整的VCDU数据区
                            'frame_len': int(frame['frame_len']),
                            'frame_sync_status': int(frame['帧同步状态']),
                            'bit_slip_status': int(frame['位滑状态']),
                            '帧检验结果': int(frame['帧检验结果']),
                            '同步字状态': int(frame['同步字状态'])
                        })
                
                processed += frames_to_read
                progress = int(50 + (processed / total_frames * 40))
                progress_callback(progress, f"已处理 {processed}/{total_frames} 帧", "分块读取中")
                
                # 检查内存
                if not self.check_memory_usage():
                    gc.collect()
        
        return frames
    
    def cancel(self):
        """取消操作"""
        self.logger.warning("用户取消了文件加载操作")
        self.is_cancelled = True 